Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add backup page #594

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open

feat: add backup page #594

wants to merge 7 commits into from

Conversation

gamalielhere
Copy link
Contributor

@gamalielhere gamalielhere commented Jan 8, 2025

Summary by CodeRabbit

  • New Features

    • Added a new backup detection flow for wallet restoration.
    • Introduced a new route for selecting wallet backups.
    • Enhanced routing logic for wallet creation and restoration processes.
    • Implemented a backup functionality during wallet initialization and account addition.
    • Added a new component for selecting and using a backup.
    • Introduced a new property to differentiate test wallets in account management.
    • Added a new section in settings for managing backups.
    • Introduced a new component for backup settings management.
  • Bug Fixes

    • Updated navigation flow after password validation in wallet restoration.
  • Chores

    • Refined UI routing and component configurations for onboarding experience.
    • Updated dependencies for improved functionality and security.
    • Introduced a new constant for backup URL configuration.

Copy link

coderabbitai bot commented Jan 8, 2025

Walkthrough

This pull request introduces changes to the wallet restoration and onboarding flow in the extension's user interface. The modifications include adding a new backup detection route and component, updating routing logic in the password typing component, and adjusting the main App.vue to handle the new route. The changes primarily focus on enhancing the user experience during wallet restoration by introducing a step to detect and select existing backups before wallet initialization. Additionally, new functionalities for managing backup states and handling encryption have been added.

Changes

File Change Summary
packages/extension/src/ui/onboard/App.vue Updated isShowBackButton and wrapClassObject functions to handle new route 'restore-wallet-backup-detected'
packages/extension/src/ui/onboard/create-wallet/routes.ts Added new route configurations with namespace prefixes for wallet creation routes
packages/extension/src/ui/onboard/restore-wallet/backup-detected.vue New component for selecting and using wallet backups
packages/extension/src/ui/onboard/restore-wallet/routes.ts Added new route for backup detection with path and component
packages/extension/src/ui/onboard/restore-wallet/type-password.vue Modified routing after password validation to navigate to backup detection route
packages/extension/src/libs/backup-state/configs.ts Introduced new constant BACKUP_URL for backup operations
packages/extension/src/libs/backup-state/index.ts Added BackupState class with methods for managing backup operations
packages/extension/src/libs/backup-state/types.ts Added new types and an enum for managing backup-related data
packages/extension/src/libs/keyring/public-keyring.ts Introduced isTestWallet property to account objects in getKeysObject method
packages/extension/src/libs/utils/initialize-wallet.ts Updated account initialization to filter out test wallets and modified return type of onboardInitializeWallets function
packages/extension/src/providers/ethereum/networks/ftm.ts Changed node property from HTTP to WebSocket endpoint for Fantom network
packages/extension/src/types/provider.ts Added backupState entry to InternalStorageNamespace enum
packages/extension/src/ui/action/App.vue Created an instance of BackupState and called backup method on mount
packages/extension/src/ui/action/views/accounts/components/add-account-form.vue Called backup method of BackupState after adding a new account
packages/extension/src/ui/action/views/swap/libs/send-transactions.ts Updated type annotations in executeSwap function
packages/signers/ethereum/src/index.ts Consolidated imports related to encrypted data handling from @enkryptcom/utils
packages/signers/ethereum/src/utils.ts Removed utility functions for encrypted data handling
packages/types/src/index.ts Added optional isTestWallet property to EnkryptAccount interface
packages/utils/package.json Added tweetnacl-util as a new development dependency
packages/utils/src/index.ts Introduced new imports and exports for encryption functionalities
packages/utils/src/nacl-encrypt-decrypt.ts Implemented encryption and decryption functionalities using NaCl
packages/extension/src/ui/action/views/settings/components/settings-inner-header.vue Added isBackups property to handle backup state in settings header
packages/extension/src/ui/action/views/settings/index.vue Introduced backupsAction method and conditionally rendered settings-backups component
packages/extension/src/ui/action/views/settings/views/settings-backups/index.vue New component for backup settings
packages/extension/src/ui/action/views/settings/views/settings-general/index.vue Added settings-button component to access backup settings
packages/extension/src/ui/onboard/create-wallet/store.ts Renamed store from restoreWallet to newWallet
packages/extension/src/ui/onboard/create-wallet/wallet-ready.vue Imported and utilized useRestoreStore and useOnboardStore for state management

Suggested reviewers

  • NickKelly1
  • olgakup
  • SemajaM8

Tip

🌐 Web search-backed reviews and chat
  • We have enabled web search-based reviews and chat for all users. This feature allows CodeRabbit to access the latest documentation and information on the web.
  • You can disable this feature by setting web_search: false in the knowledge_base settings.
  • Please share any feedback in the Discord discussion.
✨ Finishing Touches
  • 📝 Generate Docstrings (Beta)

Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link

github-actions bot commented Jan 8, 2025

💼 Build Files
chrome: enkrypt-chrome-e1c02d1a.zip
firefox: enkrypt-firefox-e1c02d1a.zip

💉 Virus total analysis
chrome: e1c02d1a
firefox: e1c02d1a

@gamalielhere gamalielhere changed the base branch from main to develop January 8, 2025 19:53
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (1)
packages/extension/src/ui/onboard/restore-wallet/backup-detected.vue (1)

87-89: Avoid fixed height containers

Using a fixed height for the backup items container might cause issues with overflow when there are many backups.

   &__backup-items-container {
-    height: 150px;
+    max-height: 150px;
+    overflow-y: auto;
   }
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 793d0f0 and d2e39c1.

📒 Files selected for processing (5)
  • packages/extension/src/ui/onboard/App.vue (2 hunks)
  • packages/extension/src/ui/onboard/create-wallet/routes.ts (1 hunks)
  • packages/extension/src/ui/onboard/restore-wallet/backup-detected.vue (1 hunks)
  • packages/extension/src/ui/onboard/restore-wallet/routes.ts (2 hunks)
  • packages/extension/src/ui/onboard/restore-wallet/type-password.vue (1 hunks)
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: buildAll
  • GitHub Check: test
🔇 Additional comments (3)
packages/extension/src/ui/onboard/create-wallet/routes.ts (1)

Line range hint 1-8: LGTM!

The route configuration is well-structured and follows Vue Router conventions.

packages/extension/src/ui/onboard/restore-wallet/routes.ts (1)

41-45: Verify navigation flow for backup detection

The backup detection route is correctly added, but let's verify the navigation flow:

  1. Should this route be accessible directly?
  2. Should users be able to navigate back from this route?

Run this script to analyze the navigation flow:

✅ Verification successful

Backup detection route follows secure navigation pattern

The route is correctly implemented with proper navigation guards:

  • Only accessible through the password verification flow
  • Cannot be directly accessed, maintaining security
  • Follows the established wallet restoration pattern
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Check for route guards and navigation patterns
rg -A 3 "router\.(push|replace)" packages/extension/src/ui/onboard/

Length of output: 8072

packages/extension/src/ui/onboard/App.vue (1)

49-49: LGTM! Back button visibility logic is consistent.

The addition of restore-wallet-backup-detected to the list of routes where the back button should be hidden follows the existing pattern for terminal states in the workflow.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

♻️ Duplicate comments (1)
packages/extension/src/ui/onboard/restore-wallet/backup-detected.vue (1)

8-19: 🛠️ Refactor suggestion

Improve accessibility for backup selection.

The backup selection UI needs accessibility improvements:

  1. Using anchor tags (<a>) for clickable elements without href
  2. Missing proper ARIA attributes
  3. No keyboard navigation support
-      <a
+      <button
+        type="button"
+        role="option"
+        :aria-selected="selectedBackup === backup"
+        tabindex="0"
         v-for="backup in backups"
         :key="backup.userId"
         @click="selectBackup(backup)"
         :class="[
           { selected: selectedBackup === backup },
           'backup-detected__backup-item',
         ]"
       >
         {{ new Date(backup.updatedAt).toLocaleString() }}
-      </a>
+      </button>
🧹 Nitpick comments (8)
packages/utils/src/nacl-encrypt-decrypt.ts (1)

35-38: Fix typo in variable naming.

The variable name "recieverPrivateKeyUint8Array"/"recieverEncryptionPrivateKey" is misspelled. Consider renaming it to "receiver" for clarity and consistency.

- const recieverPrivateKeyUint8Array = naclDecodeHex(privateKey);
- const recieverEncryptionPrivateKey = ...
+ const receiverPrivateKeyUint8Array = naclDecodeHex(privateKey);
+ const receiverEncryptionPrivateKey = ...
packages/extension/src/libs/backup-state/index.ts (3)

54-63: Add error handling for failed fetch.

If the network request fails or the response is not OK, this code may throw an unhandled exception. Consider wrapping it in a try/catch or checking response.ok before calling json().

- const rawResponse = await fetch(`${BACKUP_URL}backups/${pubkey}`, {
+ let rawResponse;
+ try {
+   rawResponse = await fetch(`${BACKUP_URL}backups/${pubkey}`, {
      method: 'GET',
      headers: {
        Accept: 'application/json',
        'Content-Type': 'application/json',
      },
    });
+   if (!rawResponse.ok) {
+     throw new Error(`Failed request with status: ${rawResponse.status}`);
+   }
+ } catch (error) {
+   console.error(error);
+   throw new Error("Could not complete backup retrieval.");
+ }

132-135: Consider deferring “continue” with an if/else chain.

The continue statement is flagged as unnecessary by linters. You can restructure the flow with if/else blocks instead of using continue. While this code works, removing or refactoring it might simplify readability.

if (existingAccount && newAccount) {
  console.log('Account already exists, just renaming');
  await kr.renameAccount(existingAccount.address, newAccount.name);
- continue;
} else if (newAccount) {
🧰 Tools
🪛 Biome (1.9.4)

[error] 135-135: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)


215-228: Wrap POST request in try/catch for robust error handling.

Like with other network calls, a failure here can result in an unhandled rejection. Consider adding error handling similar to the getBackups method to gracefully handle errors.

- const rawResponse = await fetch(
+ let rawResponse;
+ try {
+   rawResponse = await fetch(
      `${BACKUP_URL}backups/${mainWallet.publicKey}/${state.userId}`,
      {
        method: 'POST',
        headers: {
          Accept: 'application/json',
          'Content-Type': 'application/json',
        },
        body: JSON.stringify({
          payload: encryptedStr,
          signature: JSON.parse(res.result as string),
        }),
      },
    );
+   if (!rawResponse.ok) {
+     throw new Error(`Backup POST request failed: ${rawResponse.status}`);
+   }
+ } catch (error) {
+   console.error(error);
+   return false;
+ }
packages/extension/src/ui/onboard/restore-wallet/backup-detected.vue (2)

78-95: Enhance error handling with user feedback.

The error handling in the backup restoration process could be improved by:

  1. Providing specific error messages based on the error type
  2. Adding a retry mechanism for failed restorations
 const useBackup = async () => {
   if (selectedBackup.value) {
     backupBtnText.value = 'Restoring backup...';
     processing.value = true;
     await backupState
       .restoreBackup(selectedBackup.value, password)
       .then(() => {
         router.push({
           name: routes.walletReady.name,
         });
       })
-      .catch(() => {
-        backupBtnText.value = 'Failed to restore backup';
+      .catch((error) => {
+        const errorMessage = error?.message || 'Failed to restore backup';
+        backupBtnText.value = errorMessage;
         processing.value = false;
         selectedBackup.value = undefined;
+        // Optional: Add retry mechanism
+        setTimeout(() => {
+          backupBtnText.value = 'Use backup';
+        }, 3000);
       });
   }
 };

136-138: Consider dynamic height for backup items container.

The fixed height of 150px for the backup items container might cause overflow issues with many backups.

   &__backup-items-container {
-    height: 150px;
+    max-height: 150px;
+    overflow-y: auto;
   }
packages/extension/src/ui/action/views/accounts/components/add-account-form.vue (1)

124-127: Enhance backup error handling and user feedback.

The backup error handling should:

  1. Notify the user of backup success/failure
  2. Consider retry mechanism for failed backups
  3. Log more detailed error information
     const backupState = new BackupState();
-    backupState.backup(false).catch(() => {
-      console.error('Failed to backup');
-    });
+    backupState.backup(false)
+      .then(() => {
+        // Optional: Notify user of successful backup
+        console.log('Backup successful');
+      })
+      .catch((error) => {
+        console.error('Backup failed:', error);
+        // Optional: Show error notification to user
+        // Optional: Implement retry mechanism
+      });
packages/extension/src/types/provider.ts (1)

134-134: Remove unnecessary constructor.

The constructor is unnecessary as it only contains an empty implementation and doesn't override or extend any parent class functionality.

-  constructor(node: string, options?: unknown) {}
🧰 Tools
🪛 Biome (1.9.4)

[error] 134-134: This constructor is unnecessary.

Unsafe fix: Remove the unnecessary constructor.

(lint/complexity/noUselessConstructor)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d2e39c1 and e81cd7b.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (18)
  • packages/extension/src/libs/backup-state/configs.ts (1 hunks)
  • packages/extension/src/libs/backup-state/index.ts (1 hunks)
  • packages/extension/src/libs/backup-state/types.ts (1 hunks)
  • packages/extension/src/libs/keyring/public-keyring.ts (8 hunks)
  • packages/extension/src/libs/utils/initialize-wallet.ts (2 hunks)
  • packages/extension/src/providers/ethereum/networks/ftm.ts (1 hunks)
  • packages/extension/src/types/provider.ts (2 hunks)
  • packages/extension/src/ui/action/App.vue (3 hunks)
  • packages/extension/src/ui/action/views/accounts/components/add-account-form.vue (2 hunks)
  • packages/extension/src/ui/action/views/swap/libs/send-transactions.ts (15 hunks)
  • packages/extension/src/ui/onboard/restore-wallet/backup-detected.vue (1 hunks)
  • packages/extension/src/ui/onboard/restore-wallet/type-password.vue (1 hunks)
  • packages/signers/ethereum/src/index.ts (1 hunks)
  • packages/signers/ethereum/src/utils.ts (0 hunks)
  • packages/types/src/index.ts (1 hunks)
  • packages/utils/package.json (1 hunks)
  • packages/utils/src/index.ts (2 hunks)
  • packages/utils/src/nacl-encrypt-decrypt.ts (1 hunks)
💤 Files with no reviewable changes (1)
  • packages/signers/ethereum/src/utils.ts
✅ Files skipped from review due to trivial changes (1)
  • packages/extension/src/libs/backup-state/configs.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/extension/src/ui/onboard/restore-wallet/type-password.vue
🧰 Additional context used
🪛 Biome (1.9.4)
packages/extension/src/types/provider.ts

[error] 134-134: This constructor is unnecessary.

Unsafe fix: Remove the unnecessary constructor.

(lint/complexity/noUselessConstructor)

packages/extension/src/libs/backup-state/index.ts

[error] 135-135: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)

packages/extension/src/ui/action/views/swap/libs/send-transactions.ts

[error] 243-243: Unnecessary label.

Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.

(lint/complexity/noUselessLabel)


[error] 354-354: Unnecessary label.

Safe fix: Remove the unnecessary label.
You can achieve the same result without the label.

(lint/complexity/noUselessLabel)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: buildAll
  • GitHub Check: test
🔇 Additional comments (14)
packages/utils/src/nacl-encrypt-decrypt.ts (1)

42-48: Good use of try/catch for null decryption edge cases.

If naclBox.open() fails, it returns null, which is effectively caught by this block. This is a robust pattern ensuring errors in decryption do not cause undefined behavior.

packages/extension/src/libs/backup-state/types.ts (1)

1-25: All types and interfaces appear consistent and well-structured.

packages/utils/src/index.ts (2)

14-20: LGTM! Good consolidation of encryption utilities.

The imports are well-organized and the encryption-related functions are properly consolidated.


52-56: LGTM! Consistent export of new utilities.

The exports are properly organized alongside existing utilities.

packages/signers/ethereum/src/index.ts (1)

11-17: LGTM! Good refactoring of imports.

Moving to centralized utilities from @enkryptcom/utils improves code organization.

packages/extension/src/libs/utils/initialize-wallet.ts (1)

11-25: LGTM! Good filtering of test wallets.

The filter implementation for test wallets is clean and consistent across all networks.

packages/types/src/index.ts (1)

74-74: LGTM!

The optional isTestWallet property is correctly added to the EnkryptAccount interface, maintaining backward compatibility.

packages/extension/src/libs/keyring/public-keyring.ts (1)

26-26: LGTM!

The isTestWallet property is consistently added to all test accounts and is safely contained within the development mode condition.

Also applies to: 37-37, 48-48, 59-59, 70-70, 84-84, 95-95, 106-106

packages/extension/src/types/provider.ts (1)

56-56: LGTM! New backup state namespace added.

The addition of backupState to InternalStorageNamespace enum supports the new backup functionality.

packages/extension/src/ui/action/views/swap/libs/send-transactions.ts (2)

78-78: LGTM! Improved type annotations consistency.

Changed type annotations to use semicolons instead of commas, following TypeScript best practices.

Also applies to: 181-181, 507-507


221-223: LGTM! Enhanced string concatenation readability.

Added consistent spacing in string concatenations to improve code readability.

Also applies to: 331-334, 341-343, 370-372, 437-439, 457-458

packages/extension/src/ui/action/App.vue (2)

195-195: LGTM! Added BackupState initialization.

Added BackupState import and instance creation to support backup functionality.

Also applies to: 201-201


426-426: LGTM! Added backup operation with error handling.

The backup operation is correctly placed after initialization and includes error handling. It's also properly guarded by the keyring lock check.

packages/utils/package.json (1)

46-46: Verify the tweetnacl-util package version.

The addition of tweetnacl-util is for cryptographic operations. Let's verify if this is the latest stable version.

✅ Verification successful

🌐 Web query:

What is the latest version of tweetnacl-util?

Length of output: 3228


Tweetnacl-util package version is verified as current.

  • The version specified ("^0.15.1") in packages/utils/package.json matches the latest stable release on npm (v0.15.1, published January 29, 2020).
  • There are no updates available, so no changes are needed.

packages/extension/src/providers/ethereum/networks/ftm.ts Outdated Show resolved Hide resolved
packages/extension/src/libs/utils/initialize-wallet.ts Outdated Show resolved Hide resolved
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
packages/extension/src/libs/utils/initialize-wallet.ts (1)

69-81: 🛠️ Refactor suggestion

Add specific error handling for unlock operation.

While the try-catch block is good, we should verify the unlock status and handle specific error cases.

  try {
-    await kr.unlock(password);
+    const unlocked = await kr.unlock(password);
+    if (!unlocked) {
+      console.error('Failed to unlock keyring');
+      return { backupsFound: false };
+    }
     const mainAccount = await kr.getNewAccount({
🧹 Nitpick comments (1)
packages/extension/src/libs/utils/initialize-wallet.ts (1)

11-25: Consider extracting the repeated filtering logic.

The test wallet filtering logic is duplicated across all network account retrievals. Consider extracting this into a helper function to improve maintainability and reduce duplication.

+const filterNonTestWallets = async (networkName: NetworkNames) => {
+  const accounts = await getAccountsByNetworkName(networkName);
+  return accounts.filter(acc => !acc.isTestWallet);
+};

-  const secp256k1btc = (
-    await getAccountsByNetworkName(NetworkNames.Bitcoin)
-  ).filter(acc => !acc.isTestWallet);
+  const secp256k1btc = await filterNonTestWallets(NetworkNames.Bitcoin);
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 40d095c and 274981e.

📒 Files selected for processing (3)
  • packages/extension/src/libs/utils/initialize-wallet.ts (2 hunks)
  • packages/extension/src/ui/onboard/App.vue (4 hunks)
  • packages/extension/src/ui/onboard/restore-wallet/type-password.vue (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/extension/src/ui/onboard/restore-wallet/type-password.vue
  • packages/extension/src/ui/onboard/App.vue
⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: buildAll
  • GitHub Check: test
🔇 Additional comments (1)
packages/extension/src/libs/utils/initialize-wallet.ts (1)

76-77: LGTM! Proper resource cleanup.

Good practice to lock the KeyRing after use, ensuring proper resource cleanup.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 5

🧹 Nitpick comments (7)
packages/extension/src/ui/onboard/create-wallet/wallet-ready.vue (1)

47-52: Consider using more secure methods to clear sensitive data.

While clearing sensitive data is good practice, using empty strings might not be the most secure approach. Consider using secure string overwriting or dedicated secure memory clearing functions.

Example approach:

 onMounted(() => {
-  onboardStore.setPassword('');
-  onboardStore.setMnemonic('');
-  restoreStore.setMnemonic('');
-  restoreStore.setPassword('');
+  // Use a secure string overwriting approach
+  const securelyOverwrite = (str: string) => {
+    if (typeof str === 'string') {
+      const buffer = new Uint8Array(str.length);
+      crypto.getRandomValues(buffer);
+      return String.fromCharCode.apply(null, buffer);
+    }
+    return '';
+  };
+  
+  onboardStore.setPassword(securelyOverwrite(onboardStore.password));
+  onboardStore.setMnemonic(securelyOverwrite(onboardStore.mnemonic));
+  restoreStore.setMnemonic(securelyOverwrite(restoreStore.mnemonic));
+  restoreStore.setPassword(securelyOverwrite(restoreStore.password));
 });
packages/extension/src/ui/onboard/create-wallet/store.ts (1)

15-15: Consider adding TypeScript types for store values.

Adding explicit types for the store's return values would improve type safety and developer experience.

+interface OnboardStore {
+  mnemonic: Ref<string>;
+  password: Ref<string>;
+  setMnemonic: (mnemonic: string) => void;
+  setPassword: (password: string) => void;
+}

-  return { mnemonic, password, setMnemonic, setPassword };
+  return { mnemonic, password, setMnemonic, setPassword } as OnboardStore;
packages/extension/src/ui/action/views/settings/views/settings-backups/index.vue (1)

1-16: Add component documentation.

Add JSDoc comments to document the component's purpose and functionality.

Add documentation at the top of the file:

+/**
+ * Settings Backups Component
+ * 
+ * Provides interface for managing wallet backups.
+ * Allows users to enable/disable automatic backups and manage backup settings.
+ */
 <template>
packages/extension/src/ui/action/views/settings/index.vue (1)

131-145: Fix duplicate height property in CSS.

The settings__wrap class has two height declarations. The second one overrides the first one.

Remove the duplicate height property:

   &__wrap {
     background: @white;
     box-shadow:
       0px 3px 6px rgba(0, 0, 0, 0.039),
       0px 7px 24px rgba(0, 0, 0, 0.19);
     border-radius: 12px;
     box-sizing: border-box;
     width: 460px;
-    height: auto;
     z-index: 107;
     position: relative;
     height: 568px;
     overflow-x: hidden;
     padding-bottom: 16px;
   }
packages/extension/src/libs/backup-state/types.ts (2)

7-11: Consider modern TypeScript naming conventions.

The I prefix for interfaces is an older convention. Modern TypeScript style guides recommend dropping the I prefix.

-export interface IState {
+export interface State {
   lastBackupTime: number;
   userId: string;
   enabled: boolean;
 }

13-17: Consider using Date type for updatedAt.

The updatedAt property represents a date/time value. Consider using the Date type for better type safety and to leverage built-in date manipulation methods.

 export interface BackupType {
   userId: string;
   payload: string;
-  updatedAt: string;
+  updatedAt: Date;
 }
packages/extension/src/libs/backup-state/index.ts (1)

33-47: Consider extracting wallet filter logic and improving error message.

The wallet filtering logic could be extracted into a separate method for better readability. Also, the error message could be more descriptive.

 async getMainWallet(): Promise<EnkryptAccount> {
   const pkr = new PublicKeyRing();
   const allAccounts = await pkr.getAccounts();
-  const mainWallet = allAccounts.find(
-    acc =>
-      acc.walletType === 'mnemonic' &&
-      acc.pathIndex === 0 &&
-      acc.signerType === 'secp256k1' &&
-      acc.basePath === EthNetwork.basePath,
-  );
+  const mainWallet = allAccounts.find(this.isMainWallet);
   if (!mainWallet) {
-    throw new Error('No main wallet found');
+    throw new Error('No main wallet found. Ensure a mnemonic wallet with index 0 exists.');
   }
   return mainWallet;
 }
+
+private isMainWallet(acc: EnkryptAccount): boolean {
+  return (
+    acc.walletType === 'mnemonic' &&
+    acc.pathIndex === 0 &&
+    acc.signerType === 'secp256k1' &&
+    acc.basePath === EthNetwork.basePath
+  );
+}
📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 274981e and 00e4e72.

📒 Files selected for processing (8)
  • packages/extension/src/libs/backup-state/index.ts (1 hunks)
  • packages/extension/src/libs/backup-state/types.ts (1 hunks)
  • packages/extension/src/ui/action/views/settings/components/settings-inner-header.vue (2 hunks)
  • packages/extension/src/ui/action/views/settings/index.vue (5 hunks)
  • packages/extension/src/ui/action/views/settings/views/settings-backups/index.vue (1 hunks)
  • packages/extension/src/ui/action/views/settings/views/settings-general/index.vue (3 hunks)
  • packages/extension/src/ui/onboard/create-wallet/store.ts (1 hunks)
  • packages/extension/src/ui/onboard/create-wallet/wallet-ready.vue (1 hunks)
🧰 Additional context used
🪛 Biome (1.9.4)
packages/extension/src/libs/backup-state/index.ts

[error] 135-135: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: buildAll
  • GitHub Check: test
🔇 Additional comments (10)
packages/extension/src/ui/onboard/create-wallet/wallet-ready.vue (2)

40-46: LGTM! Clean store management setup.

The imports and store initialization follow Vue 3 composition API best practices.


9-20: Verify social media links alignment with brand identity.

There appears to be an inconsistency in branding:

  • Social media links point to MyEtherWallet accounts
  • Blog link points to enkrypt.com

This might confuse users about the brand identity.

Please confirm if the social media links should be updated to point to Enkrypt's social media accounts instead of MyEtherWallet's.

Also applies to: 26-27

packages/extension/src/ui/action/views/settings/components/settings-inner-header.vue (1)

11-11: LGTM!

The changes follow the existing pattern for header titles and the prop definition follows Vue.js best practices.

Also applies to: 47-50

packages/extension/src/ui/action/views/settings/index.vue (1)

42-47: LGTM!

The implementation of the backups functionality follows the existing patterns for component management and state handling. The navigation flow is correct with the back button returning to general settings.

Also applies to: 60-60, 68-68, 84-84, 117-120

packages/extension/src/ui/action/views/settings/views/settings-general/index.vue (1)

52-52: LGTM!

The addition of the backups button and event handling follows the existing patterns and is well-integrated into the settings interface.

Also applies to: 70-70, 82-84

packages/extension/src/libs/backup-state/types.ts (3)

3-5: LGTM!

The enum follows TypeScript best practices and uses descriptive naming.


18-20: LGTM!

The interface is well-structured and follows TypeScript best practices.


22-25: LGTM!

The interface is well-structured and makes good use of TypeScript's Omit utility type.

packages/extension/src/libs/backup-state/index.ts (2)

26-31: LGTM!

The class structure follows TypeScript best practices with proper encapsulation.


247-278: LGTM!

The state management methods are well-structured, concise, and follow TypeScript best practices.

Comment on lines 5 to 6
const mnemonic = ref('');
const password = ref('');
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Enhance security for sensitive data handling.

The store currently holds sensitive data (mnemonic and password) in plain text refs. Consider implementing:

  1. Memory cleanup after use
  2. Encryption for sensitive data in memory
  3. Validation for password strength

Here's a suggested implementation:

 import { defineStore } from 'pinia';
 import { ref } from 'vue';
+import { encrypt, decrypt } from '../utils/crypto';
+import { validatePassword } from '../utils/validation';

 export const useOnboardStore = defineStore('newWallet', () => {
-  const mnemonic = ref('');
-  const password = ref('');
+  const encryptedMnemonic = ref('');
+  const encryptedPassword = ref('');
 
   const setMnemonic = (_mnemonic: string) => {
-    mnemonic.value = _mnemonic;
+    encryptedMnemonic.value = encrypt(_mnemonic);
   };
+
   const setPassword = (_password: string) => {
-    password.value = _password;
+    if (!validatePassword(_password)) {
+      throw new Error('Password does not meet security requirements');
+    }
+    encryptedPassword.value = encrypt(_password);
   };
+
+  const cleanup = () => {
+    encryptedMnemonic.value = '';
+    encryptedPassword.value = '';
+  };
 
-  return { mnemonic, password, setMnemonic, setPassword };
+  return { 
+    getMnemonic: () => decrypt(encryptedMnemonic.value),
+    getPassword: () => decrypt(encryptedPassword.value),
+    setMnemonic, 
+    setPassword,
+    cleanup 
+  };
 });

Also applies to: 8-13

@@ -1,7 +1,7 @@
import { defineStore } from 'pinia';
import { ref } from 'vue';

export const useOnboardStore = defineStore('restoreWallet', () => {
export const useOnboardStore = defineStore('newWallet', () => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Store name change indicates broader functionality.

The store name change from 'restoreWallet' to 'newWallet' suggests this store now handles both new wallet creation and restoration. Ensure this aligns with the intended architecture and that all components using this store have been updated accordingly.

Let's verify all usages of this store to ensure the rename doesn't break existing functionality:


🏁 Script executed:

#!/bin/bash
# Search for any remaining references to the old store name
rg "restoreWallet" --type ts

# Search for all imports of this store to verify updates
rg "useOnboardStore" --type ts

Length of output: 566

Store Name Inconsistency Detected

  • The file packages/extension/src/ui/onboard/create-wallet/store.ts defines its store using the ID 'newWallet', while the file packages/extension/src/ui/action/components/app-menu/store.ts still defines a store (exported as useOnboardStore) with the ID 'restoreWallet'.
  • Additionally, the existence of packages/extension/src/ui/onboard/restore-wallet/store.ts further indicates remnants of the old naming pattern.
  • This inconsistency could lead to unexpected behavior in the wallet onboarding and restoration flows. Ensure that the store renaming is applied consistently across all components and that any related functionality correctly references the intended store.

<div>
<settings-inner-header v-bind="$attrs" :is-backups="true" />

<settings-button title="Enable backups" :is-link="true" />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Add click handler for the Enable backups button.

The button appears to be non-functional as it's missing a click handler to implement the backup functionality.

Add click handler and implement backup functionality:

-    <settings-button title="Enable backups" :is-link="true" />
+    <settings-button 
+      title="Enable backups" 
+      :is-link="true" 
+      @click="handleEnableBackups" 
+    />

And in the script section:

 <script setup lang="ts">
 import SettingsInnerHeader from '@action/views/settings/components/settings-inner-header.vue';
 import SettingsButton from '@action/views/settings/components/settings-button.vue';
+
+const handleEnableBackups = () => {
+  // TODO: Implement backup functionality
+};
 </script>
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<settings-button title="Enable backups" :is-link="true" />
<template>
<settings-button
title="Enable backups"
:is-link="true"
@click="handleEnableBackups"
/>
</template>
<script setup lang="ts">
import SettingsInnerHeader from '@action/views/settings/components/settings-inner-header.vue';
import SettingsButton from '@action/views/settings/components/settings-button.vue';
const handleEnableBackups = () => {
// TODO: Implement backup functionality
};
</script>

Comment on lines +65 to +158
async restoreBackup(
backup: BackupType,
keyringPassword: string,
): Promise<void> {
const mainWallet = await this.getMainWallet();
await sendUsingInternalMessengers({
method: InternalMethods.unlock,
params: [keyringPassword, false],
});
await sendUsingInternalMessengers({
method: InternalMethods.ethereumDecrypt,
params: [backup.payload, mainWallet],
}).then(async res => {
if (res.error) {
console.error(res);
return null;
} else {
const kr = new KeyRingBase();
await kr.unlock(keyringPassword);
const existingAccounts = await kr.getKeysArray();
const decryptedBackup: BackupData = JSON.parse(
JSON.parse(res.result as string),
);
console.log(decryptedBackup);
const highestPathIndex: Record<string, number> = {};
decryptedBackup.accounts.forEach(acc => {
const id = `${acc.basePath}###${acc.signerType}`;
const idx = acc.pathIndex;
if (!highestPathIndex[id] || highestPathIndex[id] < idx) {
highestPathIndex[id] = idx;
}
});

console.log(highestPathIndex);
const getAccountByIndex = (
accounts: Omit<EnkryptAccount, 'address' | 'publicKey'>[],
basePath: string,
signerType: SignerType,
idx: number,
): EnkryptAccount | null => {
for (const acc of accounts) {
if (
acc.basePath === basePath &&
acc.pathIndex === idx &&
acc.signerType === signerType
) {
return acc as EnkryptAccount;
}
}
return null;
};

for (const key of Object.keys(highestPathIndex)) {
const [basePath, signerType] = key.split('###');
for (let i = 0; i <= highestPathIndex[key]; i++) {
const newAccount = getAccountByIndex(
decryptedBackup.accounts,
basePath,
signerType as SignerType,
i,
);
const existingAccount = getAccountByIndex(
existingAccounts,
basePath,
signerType as SignerType,
i,
);
if (existingAccount && newAccount) {
console.log('Account already exists, just renaming');
await kr.renameAccount(existingAccount.address, newAccount.name);
continue;
} else if (newAccount) {
console.log('creating new account', newAccount);
await kr.saveNewAccount({
basePath: newAccount.basePath,
name: newAccount.name,
signerType: newAccount.signerType,
walletType: newAccount.walletType,
});
} else if (!newAccount) {
console.log('edge case shouldnt happen', newAccount);
await kr.saveNewAccount({
basePath: basePath,
name: `New Account from backup ${i}`,
signerType: signerType as SignerType,
walletType: WalletType.mnemonic,
});
}
}
}
await this.setUserId(decryptedBackup.uuid);
}
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Method needs refactoring for maintainability and error handling.

The method is quite long and complex. Consider the following improvements:

  1. Remove debug console.log statements
  2. Extract account handling logic into separate methods
  3. Improve error handling and messaging
  4. Add input validation
 async restoreBackup(
   backup: BackupType,
   keyringPassword: string,
 ): Promise<void> {
+  if (!backup || !keyringPassword) {
+    throw new Error('Invalid backup or keyring password');
+  }
   const mainWallet = await this.getMainWallet();
   await sendUsingInternalMessengers({
     method: InternalMethods.unlock,
     params: [keyringPassword, false],
   });
-  await sendUsingInternalMessengers({
+  const decryptResult = await sendUsingInternalMessengers({
     method: InternalMethods.ethereumDecrypt,
     params: [backup.payload, mainWallet],
-  }).then(async res => {
-    if (res.error) {
-      console.error(res);
-      return null;
-    } else {
-      const kr = new KeyRingBase();
-      // ... rest of the code
-    }
   });
+  
+  if (decryptResult.error) {
+    throw new Error(`Failed to decrypt backup: ${decryptResult.error}`);
+  }
+  
+  const kr = new KeyRingBase();
+  await kr.unlock(keyringPassword);
+  
+  const decryptedBackup = this.parseBackupData(decryptResult.result as string);
+  await this.restoreAccounts(kr, decryptedBackup);
+  await this.setUserId(decryptedBackup.uuid);
 }
+
+private parseBackupData(result: string): BackupData {
+  try {
+    return JSON.parse(JSON.parse(result));
+  } catch (error) {
+    throw new Error(`Failed to parse backup data: ${error.message}`);
+  }
+}

Committable suggestion skipped: line range outside the PR's diff.

🧰 Tools
🪛 Biome (1.9.4)

[error] 135-135: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)

Comment on lines 160 to 245
async backup(firstTime: boolean): Promise<boolean> {
const state = await this.getState();
if (firstTime && state.lastBackupTime !== 0) {
return true;
}
if (!state.enabled) {
return true;
}
const pkr = new PublicKeyRing();
const allAccounts = await pkr.getAccounts();
const mainWallet = await this.getMainWallet();
const backupData: BackupData = {
accounts: allAccounts
.filter(
acc => !acc.isTestWallet && acc.walletType !== WalletType.privkey,
)
.map(acc => {
return {
basePath: acc.basePath,
pathIndex: acc.pathIndex,
name: acc.name,
signerType: acc.signerType,
walletType: acc.walletType,
isHardware: acc.isHardware,
HWOptions: acc.HWOptions,
};
}),
uuid: state.userId,
};
const encryptPubKey = await sendUsingInternalMessengers({
method: InternalMethods.getEthereumEncryptionPublicKey,
params: [mainWallet],
}).then(res => {
if (res.error) {
console.error(res);
return null;
} else {
return JSON.parse(res.result as string);
}
});
if (!encryptPubKey) {
console.error('No encrypt public key found');
return false;
}
const encryptedStr = naclEncrypt({
publicKey: encryptPubKey,
data: JSON.stringify(backupData),
version: NACL_VERSION,
});
const msgHash = bufferToHex(hashPersonalMessage(hexToBuffer(encryptedStr)));
return sendUsingInternalMessengers({
method: InternalMethods.sign,
params: [msgHash, mainWallet],
}).then(async res => {
if (res.error) {
console.error(res);
return false;
} else {
const rawResponse = await fetch(
`${BACKUP_URL}backups/${mainWallet.publicKey}/${state.userId}`,
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
payload: encryptedStr,
signature: JSON.parse(res.result as string),
}),
},
);
const content = await rawResponse.json();
if (content.message === 'Ok') {
await this.setState({
lastBackupTime: new Date().getTime(),
userId: state.userId,
enabled: state.enabled,
});
return true;
}
console.error(content);
return false;
}
});
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Improve error handling and logging.

The method uses console.error for error logging, which isn't ideal for production. Consider implementing proper error handling and logging.

 async backup(firstTime: boolean): Promise<boolean> {
   try {
     const state = await this.getState();
     if (firstTime && state.lastBackupTime !== 0) {
       return true;
     }
     if (!state.enabled) {
       return true;
     }
     
     const mainWallet = await this.getMainWallet();
     const encryptPubKey = await this.getEncryptionPublicKey(mainWallet);
     
     if (!encryptPubKey) {
-      console.error('No encrypt public key found');
-      return false;
+      throw new Error('No encryption public key found');
     }
     
     // ... rest of the code
     
   } catch (error) {
+    // Use proper logging service
+    throw new Error(`Backup failed: ${error.message}`);
   }
 }
+
+private async getEncryptionPublicKey(wallet: EnkryptAccount): Promise<string | null> {
+  const response = await sendUsingInternalMessengers({
+    method: InternalMethods.getEthereumEncryptionPublicKey,
+    params: [wallet],
+  });
+  
+  if (response.error) {
+    throw new Error(`Failed to get encryption public key: ${response.error}`);
+  }
+  
+  return JSON.parse(response.result as string);
+}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async backup(firstTime: boolean): Promise<boolean> {
const state = await this.getState();
if (firstTime && state.lastBackupTime !== 0) {
return true;
}
if (!state.enabled) {
return true;
}
const pkr = new PublicKeyRing();
const allAccounts = await pkr.getAccounts();
const mainWallet = await this.getMainWallet();
const backupData: BackupData = {
accounts: allAccounts
.filter(
acc => !acc.isTestWallet && acc.walletType !== WalletType.privkey,
)
.map(acc => {
return {
basePath: acc.basePath,
pathIndex: acc.pathIndex,
name: acc.name,
signerType: acc.signerType,
walletType: acc.walletType,
isHardware: acc.isHardware,
HWOptions: acc.HWOptions,
};
}),
uuid: state.userId,
};
const encryptPubKey = await sendUsingInternalMessengers({
method: InternalMethods.getEthereumEncryptionPublicKey,
params: [mainWallet],
}).then(res => {
if (res.error) {
console.error(res);
return null;
} else {
return JSON.parse(res.result as string);
}
});
if (!encryptPubKey) {
console.error('No encrypt public key found');
return false;
}
const encryptedStr = naclEncrypt({
publicKey: encryptPubKey,
data: JSON.stringify(backupData),
version: NACL_VERSION,
});
const msgHash = bufferToHex(hashPersonalMessage(hexToBuffer(encryptedStr)));
return sendUsingInternalMessengers({
method: InternalMethods.sign,
params: [msgHash, mainWallet],
}).then(async res => {
if (res.error) {
console.error(res);
return false;
} else {
const rawResponse = await fetch(
`${BACKUP_URL}backups/${mainWallet.publicKey}/${state.userId}`,
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
payload: encryptedStr,
signature: JSON.parse(res.result as string),
}),
},
);
const content = await rawResponse.json();
if (content.message === 'Ok') {
await this.setState({
lastBackupTime: new Date().getTime(),
userId: state.userId,
enabled: state.enabled,
});
return true;
}
console.error(content);
return false;
}
});
}
async backup(firstTime: boolean): Promise<boolean> {
try {
const state = await this.getState();
if (firstTime && state.lastBackupTime !== 0) {
return true;
}
if (!state.enabled) {
return true;
}
const pkr = new PublicKeyRing();
const allAccounts = await pkr.getAccounts();
const mainWallet = await this.getMainWallet();
const backupData: BackupData = {
accounts: allAccounts
.filter(
acc => !acc.isTestWallet && acc.walletType !== WalletType.privkey,
)
.map(acc => {
return {
basePath: acc.basePath,
pathIndex: acc.pathIndex,
name: acc.name,
signerType: acc.signerType,
walletType: acc.walletType,
isHardware: acc.isHardware,
HWOptions: acc.HWOptions,
};
}),
uuid: state.userId,
};
const encryptPubKey = await this.getEncryptionPublicKey(mainWallet);
if (!encryptPubKey) {
throw new Error('No encryption public key found');
}
const encryptedStr = naclEncrypt({
publicKey: encryptPubKey,
data: JSON.stringify(backupData),
version: NACL_VERSION,
});
const msgHash = bufferToHex(hashPersonalMessage(hexToBuffer(encryptedStr)));
return sendUsingInternalMessengers({
method: InternalMethods.sign,
params: [msgHash, mainWallet],
}).then(async res => {
if (res.error) {
console.error(res);
return false;
} else {
const rawResponse = await fetch(
`${BACKUP_URL}backups/${mainWallet.publicKey}/${state.userId}`,
{
method: 'POST',
headers: {
Accept: 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
payload: encryptedStr,
signature: JSON.parse(res.result as string),
}),
},
);
const content = await rawResponse.json();
if (content.message === 'Ok') {
await this.setState({
lastBackupTime: new Date().getTime(),
userId: state.userId,
enabled: state.enabled,
});
return true;
}
console.error(content);
return false;
}
});
} catch (error) {
// Use proper logging service
throw new Error(`Backup failed: ${error.message}`);
}
}
private async getEncryptionPublicKey(wallet: EnkryptAccount): Promise<string | null> {
const response = await sendUsingInternalMessengers({
method: InternalMethods.getEthereumEncryptionPublicKey,
params: [wallet],
});
if (response.error) {
throw new Error(`Failed to get encryption public key: ${response.error}`);
}
return JSON.parse(response.result as string);
}

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (3)
packages/extension/src/libs/backup-state/configs.ts (1)

1-5: Consider environment-based configuration.

Consider using environment variables for the backup URL to support different environments (development, staging, production).

packages/extension/src/ui/action/views/settings/views/settings-backups/index.vue (1)

15-47: Enhance table accessibility.

Add ARIA labels and roles to improve table accessibility:

-    <table>
+    <table role="grid" aria-label="Current Backups">
packages/extension/src/libs/backup-state/index.ts (1)

209-211: Remove unnecessary continue statement.

The continue statement at line 210 is unnecessary and can be removed.

 if (existingAccount && newAccount) {
   console.log('Account already exists, just renaming');
   await kr.renameAccount(existingAccount.address, newAccount.name);
-  continue;
 }
🧰 Tools
🪛 Biome (1.9.4)

[error] 210-210: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)

📜 Review details

Configuration used: .coderabbit.yaml
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 00e4e72 and e1c02d1.

📒 Files selected for processing (7)
  • packages/extension/src/libs/backup-state/configs.ts (1 hunks)
  • packages/extension/src/libs/backup-state/index.ts (1 hunks)
  • packages/extension/src/libs/utils/initialize-wallet.ts (2 hunks)
  • packages/extension/src/providers/ethereum/networks/ftm.ts (1 hunks)
  • packages/extension/src/ui/action/components/switch/index.vue (1 hunks)
  • packages/extension/src/ui/action/views/settings/views/settings-backups/index.vue (1 hunks)
  • packages/extension/src/ui/onboard/restore-wallet/backup-detected.vue (1 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • packages/extension/src/providers/ethereum/networks/ftm.ts
  • packages/extension/src/ui/onboard/restore-wallet/backup-detected.vue
  • packages/extension/src/libs/utils/initialize-wallet.ts
🧰 Additional context used
🪛 Biome (1.9.4)
packages/extension/src/libs/backup-state/index.ts

[error] 210-210: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)

⏰ Context from checks skipped due to timeout of 90000ms (2)
  • GitHub Check: buildAll
  • GitHub Check: test
🔇 Additional comments (4)
packages/extension/src/libs/backup-state/configs.ts (1)

1-1: ⚠️ Potential issue

Use HTTPS for secure communication.

The backup URL should use HTTPS to ensure secure data transmission.

-const BACKUP_URL = 'https://rabbit.ethvm.dev/';
+const BACKUP_URL = 'https://rabbit.ethvm.dev/';

Likely invalid or redundant comment.

packages/extension/src/ui/action/components/switch/index.vue (1)

3-3: LGTM! Good use of Vue's reactivity system.

The refactoring to use v-model with a computed property improves code maintainability and follows Vue.js best practices.

Also applies to: 18-23

packages/extension/src/libs/backup-state/index.ts (2)

140-233: 🛠️ Refactor suggestion

Refactor restoreBackup method for better maintainability.

The method is complex and could be broken down into smaller, more focused methods.

 async restoreBackup(backup: BackupType, keyringPassword: string): Promise<void> {
+  await this.validateAndUnlock(keyringPassword);
+  const decryptedBackup = await this.decryptBackup(backup);
+  await this.restoreAccounts(decryptedBackup, keyringPassword);
+  await this.setUserId(decryptedBackup.uuid);
 }

+private async validateAndUnlock(keyringPassword: string): Promise<void> {
+  await sendUsingInternalMessengers({
+    method: InternalMethods.unlock,
+    params: [keyringPassword, false],
+  });
+}

+private async decryptBackup(backup: BackupType): Promise<BackupData> {
+  const mainWallet = await this.getMainWallet();
+  const result = await sendUsingInternalMessengers({
+    method: InternalMethods.ethereumDecrypt,
+    params: [backup.payload, mainWallet],
+  });
+  
+  if (result.error) {
+    throw new Error(`Failed to decrypt backup: ${result.error}`);
+  }
+  
+  return JSON.parse(JSON.parse(result.result as string));
+}

Likely invalid or redundant comment.

🧰 Tools
🪛 Biome (1.9.4)

[error] 210-210: Unnecessary continue statement

Unsafe fix: Delete the unnecessary continue statement

(lint/correctness/noUnnecessaryContinue)


235-316: 🛠️ Refactor suggestion

Improve error handling in backup method.

The backup method should handle errors more gracefully and provide meaningful error messages.

 async backup(firstTime: boolean): Promise<boolean> {
+  try {
     const state = await this.getState();
     if (firstTime && state.lastBackupTime !== 0) {
       return true;
     }
     if (!state.enabled) {
       return true;
     }
     
     const mainWallet = await this.getMainWallet();
     const encryptPubKey = await this.getEncryptionPublicKey(mainWallet);
     
     if (!encryptPubKey) {
-      console.error('No encrypt public key found');
-      return false;
+      throw new Error('No encryption public key found');
     }
     
     // ... rest of the code
     
+  } catch (error) {
+    throw new Error(`Backup failed: ${error.message}`);
+  }
 }

Likely invalid or redundant comment.

Comment on lines +71 to +80
const toggleBackups = async (checked: boolean) => {
isBackupsEnabled.value = checked;
if (isBackupsEnabled.value) {
await backupState.enableBackups();
await backupState.backup(false);
backups.value = await backupState.getBackups();
} else {
await backupState.disableBackups();
}
};
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Add error handling for toggleBackups.

The toggleBackups method should handle potential errors from the backup operations.

 const toggleBackups = async (checked: boolean) => {
+  try {
     isBackupsEnabled.value = checked;
     if (isBackupsEnabled.value) {
       await backupState.enableBackups();
       await backupState.backup(false);
       backups.value = await backupState.getBackups();
     } else {
       await backupState.disableBackups();
     }
+  } catch (error) {
+    console.error('Failed to toggle backups:', error);
+    isBackupsEnabled.value = !checked;
+    // TODO: Show error notification to user
+  }
 };
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const toggleBackups = async (checked: boolean) => {
isBackupsEnabled.value = checked;
if (isBackupsEnabled.value) {
await backupState.enableBackups();
await backupState.backup(false);
backups.value = await backupState.getBackups();
} else {
await backupState.disableBackups();
}
};
const toggleBackups = async (checked: boolean) => {
try {
isBackupsEnabled.value = checked;
if (isBackupsEnabled.value) {
await backupState.enableBackups();
await backupState.backup(false);
backups.value = await backupState.getBackups();
} else {
await backupState.disableBackups();
}
} catch (error) {
console.error('Failed to toggle backups:', error);
isBackupsEnabled.value = !checked;
// TODO: Show error notification to user
}
};

Comment on lines +70 to +71
console.log('get backups signature', msgHash, mainWallet);
signature = await sendUsingInternalMessengers({
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Remove debug console statements.

Remove all console.log and console.error statements. Consider implementing proper error logging.

Also applies to: 77-77, 85-85, 87-87, 113-113, 123-123, 136-136, 154-154, 164-164, 173-173, 209-209, 213-213, 220-220, 270-270, 276-276, 291-291, 313-313

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants